#pragma once

#include <vex/core/config.h>
#include <vex/functional/implementation/default_value.h>
#include <vex/functional/contract/invoker_range.h>
#include <vex/functional/result.h>

namespace vex { namespace functional {

    namespace implementation {
        /// <summary>
        /// Default reducer for multicast delegates with return values of type TRet. 
        /// A reducer is any callable type which defines a unary call operator
        /// taking a <c>invoker_range</c> of TRet and returning TRet
        /// </summary>
        template<class TRet> struct default_value_reducer {
            typedef typename TRet result_type;
            typedef result_value<result_type> result_value_type;
            typedef contract::invoker_range<TRet>* argument_type;

            result_type operator()(argument_type p_reducer)
            {
                if (p_reducer) try {
                    auto t_result = m_result;
                    while (p_reducer->move_front()) {
                        t_result = p_reducer->front();
                    }
                    return t_result();
                } catch (...) {}
                return m_result();
            }

            explicit default_value_reducer(result_value_type const & p_default)
                : m_result(p_default) {}
        private:
            result_value_type m_result;
        };

        /// <summary>
        /// Default reducer for multicast delegates with void return values. 
        /// A reducer is any callable type which defines a unary call operator
        /// taking a contract::invoker_range* of void and returning void
        /// </summary> 
        template<> struct default_value_reducer<void> {
            typedef void result_type;
            typedef result_value<void> result_value_type;
            typedef contract::invoker_range<void>* argument_type;

            void operator()(argument_type p_reducer) const
            {
                if (p_reducer) try {
                    while (p_reducer->move_front()) {
                        p_reducer->front();
                    }
                } catch (...) {}
            }

            default_value_reducer(result_value_type const &) {}
        };

        template<class TDefault>
        struct make_default_reducer {
            typedef default_value_reducer<
                typename implementation::make_result_value<TDefault>::result_type
            > type;
        };

    }

    /// <summary>
    /// Creates a default reducer. A default reducer invokes each stored delegate and returns
    /// the value of the last delegate stored from current invocation list. If invocation list is empty,
    /// the default value passed as <paramref name="p_default"/> is returned. If an exception is thrown while processing the
    /// invocation list, then any further processing is cancelled an the default value is returned.
    /// </summary>
    ///
    /// <param name="p_default">a constant to be returned on invocation of empty multicast delegate</param>
    template<class TDefault> auto default_reducer(TDefault&& p_default)
        -> typename implementation::make_default_reducer<TDefault>::type
    {
        return typename implementation::make_default_reducer<TDefault>::type(
            default_value(std::forward<TDefault>(p_default)));
    }

    inline auto default_reducer()
        -> implementation::default_value_reducer<void>
    {
        return implementation::default_value_reducer<void>(default_value());
    }
}}